#pragma once

// Verifies that [T] is a HRESULT type.
template <typename T>
inline typename std::enable_if<std::is_same<T, HRESULT>::value, HRESULT>::type
__as_hresult(T hr) { return hr; }

// Verifies that [const T] can be converted to a const pointer
template <typename T>
inline typename std::enable_if<std::is_convertible<const T, const void*>::value, HRESULT>::type
__fail_if_null(const T& value) { return (value == nullptr) ? E_POINTER : S_OK; }

// Verifies that [T] can be converted to a const pointer
template <typename T>
inline typename std::enable_if<std::is_convertible<T, const void*>::value, HRESULT>::type
__fail_if_null(T& value) { return (value == nullptr) ? E_POINTER : S_OK; }

template <typename T>
HRESULT __fail_if_null(const std::unique_ptr<T>& value) { return value ? S_OK : E_POINTER; }

template <typename T>
HRESULT __fail_if_null(const std::shared_ptr<T>& value) { return value ? S_OK : E_POINTER; }


// Verifies that [T] can be treated as a boolean expression and is not an HRESULT
template <typename T>
inline typename std::enable_if<
    std::is_integral<T>::value &&
    !std::is_same<T, HRESULT>::value,
    HRESULT
>::type
__fail_if_false(const T& value) { return (value) ? S_OK : E_FAIL; }

#define RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED_COPY_MSG(function, error_message) \
    {                                                                               \
        HRESULT hr = E_FAIL;                                                        \
        if (FAILED(hr = __as_hresult(function)))                                    \
        {                                                                           \
            ErrorLogger::WriteLog(__FUNCTION__, error_message);                     \
            return hr;                                                              \
        }                                                                           \
    }

#define __FUNCTION_LINE_COPY_COPY__(x) #x
#define __FUNCTION_LINE_COPY__(x) __FUNCTION_LINE_COPY_COPY__(x)
#define __FUNCTION_LINE__ __FUNCTION_LINE_COPY__(__LINE__)

// If call function that return HRESULT type, use this micro to return E_FAIL and record a error message.
// #define RECORD_ERRORLOG_IF_FAILED_COPY(function) RECORD_ERRORLOG_IF_FAILED_MSG(function, "Failed: " #f " on line ", __LINE__)
#define RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED_COPY(function) \
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED_COPY_MSG(function, "Failed: " #function " on line " __FUNCTION_LINE__)
#define RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(function) \
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED_COPY(function)

// If call function that return a pointer, use this micro to return E_FAIL and record a error message.
#define RETURN_AND_RECORD_ERROR_MESSAGE_IF_NULL_COPY(f) \
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED_COPY_MSG(__fail_if_null(f), "Failed: " #f " was nullptr. On line " __FUNCTION_LINE__)
#define RETURN_AND_RECORD_ERROR_MESSAGE_IF_NULL(f) \
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_NULL_COPY(f)

// If call function that return a BOOL or bool value, use this micro to return E_FAIL and record a error message.
#define RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE_COPY(f) \
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED_COPY_MSG(__fail_if_false(f), "Failed: " #f " was FALSE. On line " __FUNCTION_LINE__)
#define RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE(f) \
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE_COPY(f)

const int GC_CHESS_BOARD_ROW_COUNT = 8;
const int GC_CHESS_BOARD_COL_COUNT = 8;
const int GC_CHESS_PIECE_WIDTH = 66;
const int GC_CHESS_PIECE_HEIGHT = 67;

const std::string GC_APPLICATION_NAME = "Chess";

const int INVALID_INDEX = -1;
const int INVALID_COUNT = -1;

const int PAWN_MOST_MOVE_STEP = 2;

typedef enum ENUM_PLAYER
{
    e_no_player = 0,
    e_player_1,
    e_player_2
} PLAYER;

typedef enum ENUM_CHESS_TYPE
{
    e_error_type = -1,
    e_king = 1,
    e_queue,
    e_bishop,
    e_knight,
    e_rook,
    e_pawn
} CHESS_TYPE;

typedef struct ST_CHESS_BOARD_INFORMATION
{
    ENUM_PLAYER chess_board_fill[GC_CHESS_BOARD_ROW_COUNT][GC_CHESS_BOARD_COL_COUNT];
    RECT chess_board_rect[GC_CHESS_BOARD_ROW_COUNT][GC_CHESS_BOARD_COL_COUNT];
} CHESS_BOARD_INFORMATION;

typedef struct ST_CHESS_POSITION
{
    int row_index;
    int col_index;
} CHESS_POS;